package com.gzsxy.esjy.thread;

import com.alibaba.fastjson.JSON;
import com.gzsxy.esjy.SvConstant;
import com.gzsxy.esjy.common.SvException;
import com.gzsxy.esjy.request.ContentType;
import com.gzsxy.esjy.request.ContextHolder;
import com.gzsxy.esjy.request.HttpServletRequest;

import com.gzsxy.esjy.request.impl.BaseRequest;
import com.gzsxy.esjy.response.HttpServletResponse;
import com.gzsxy.esjy.response.NettyHttpResponse;
import com.gzsxy.esjy.response.impl.DefaultHttpServletResponse;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import org.slf4j.Logger;

import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;


/**
 * 业务主线程
 * @author zhangxiaolong
 * @date 2022/7/11 9:36
 */
public final class ServiceRunnable implements Runnable{

	private final static Logger log = LoggerFactory.getLogger("http-request") ;
	
	private final ExcuteService excuteService;
	
	private final HttpServletRequest request ;
	
	private final ChannelHandlerContext context ;
	
	public ServiceRunnable(ExcuteService excuteService, HttpServletRequest request, ChannelHandlerContext context) {
		this.excuteService = excuteService ;
		this.request = request ;
		this.context = context ;
	}

	@Override
	public void run() {
		HttpServletResponse response = new DefaultHttpServletResponse(EmptyHttpHeaders.INSTANCE,context) ;
		response.setStatus(200);
		ContextHolder.putResponse(response);
		ContextHolder.putRequest(request);
//		log.info("receive request uri:{} remoteAddress:{} remoteUser:{}", request.getUri(),request.getRemoteAddress(),request.getRemoteUser());
		log.debug("receive request method:{} contentType:{} params:{}", request.getMethod().name(),request.getContentType().toString());
		BaseRequest base = ((BaseRequest)request);
		String path = ((BaseRequest)request).getPath();
		try {
			
			if(request.getMethod() == HttpMethod.POST) {
				doPostRequest(request);
			}
			FullHttpResponse create = null ;
			Object excute = excuteService.excute(((BaseRequest)request).getMappingEntity(), ((BaseRequest)request).getPath(), request.getRequestBody(), request.getParams()) ;
			
			ChannelFuture channelFuture = null  ;
			//判断为已经写出头
			if(((DefaultHttpServletResponse)response).iswrited()) {
				channelFuture = context.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT) ;
			}else {
				Object back = response.getResponse();
				int status = response.getStatus();
				HttpResponseStatus state = HttpResponseStatus.valueOf(status);
				if (back != null) {
					log.warn("response reset {} {}",path,doBack(back));
					excute = back;
				}
				if (excute == null) {
					create = NettyHttpResponse.create(state, "",request.getCharset().name());
					log.warn("response is null,return default response {} {}",path,doBack(create));
				}else {
					create = NettyHttpResponse.create(state, excute, request.getCharset().name());
				}
				channelFuture = context.writeAndFlush(create);
			}
			if(!SvConstant.KEEP_LIVE) {
				channelFuture.addListener(ChannelFutureListener.CLOSE);
			}
		}catch (Exception e) {
			ChannelFuture writeAndFlush = context.writeAndFlush(NettyHttpResponse.doExceptionResponse(e, request.getCharset().name()));
			if(!base.isKeepAlive()) {
				writeAndFlush.addListener(ChannelFutureListener.CLOSE);
			}
		}finally {
			//最终主动清楚缓存
			ContextHolder.remove();
		}
	}
	
	private String doBack(Object back) {
		if(back instanceof String) {
			return (String) back ;
		}
		return JSON.toJSONString(back) ;
	}

	/**
	 * 处理post请求
	 * @param request
	 * @return
	 * @throws Exception
	 */
	private static void doPostRequest(HttpServletRequest request) throws Exception {
		String requestBody = request.getRequestBody();
		if(StringUtils.hasText(requestBody)) {
			if(request.getContentType() == ContentType.APPLICATION_JSON) {
				return ;
			}
			if(request.getContentType() == ContentType.APPLICATION_X_WWW_FORM_URLENCODED) {
				parseParam(request);
				return ;
			}
			throw SvException.getInstance(String.format("not support contentType [%s]", request.getContentType().toString()), 400) ;
		}
	}
	
	/**
	 * 解析参数
	 * @param request
	 */
	private static void parseParam(HttpServletRequest request) {
		QueryStringDecoder decoder = new QueryStringDecoder("?"+request.getRequestBody()) ;
		Map<String, List<String>> parameters = decoder.parameters();
		for (Map.Entry<String, List<String>> attr : parameters.entrySet()) {
             for (String attrVal : attr.getValue()) {
            	 if(StringUtils.hasText(attrVal)) {
            		 request.getParams().put(attr.getKey(), attrVal);
            	 }
             }
    	}
	}

}
